package ch.cryptobox;

import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;

import javax.crypto.NoSuchPaddingException;
import org.acplt.oncrpc.OncRpcException;

import ch.cryptobox.encryption.AES;
import ch.cryptobox.encryption.KeyManager;
import ch.cryptobox.files.FileHelper;
import ch.cryptobox.files.FileSynchronizer;
import ch.cryptobox.files.FileWatcher;
import ch.cryptobox.serivces.EncryptedSyncronizationService;
import ch.cryptobox.serivces.NFSService;
import ch.cryptobox.serivces.RemoteFileService;
import ch.cryptobox.serivces.SSSEncryptionSyncronizationService;
import ch.cryptobox.serivces.SynchronizationService;
import ch.cryptobox.ui.NFSNavigator;
import ch.cryptobox.ui.NFSNavigatorGUI;



public class Main {
	final static String localCryptoBoxPath = "../../crypto-box/CryptoBox";
	final static String nfsServerCryptoBoxPath = "/etc/nfsFolder";
	final static String nfsServer = "artit-dev.sytes.net";
	final static String nfsServerCryptoBoxPath1 = "/etc/nfsFolder1";
	final static String nfsServerCryptoBoxPath2 = "/etc/nfsFolder2";
	final static String CLIENT_OPT = "-c";
	final static String SERVER_OPT = "-s";
	final static String UI_OPT = "-u";
	final static String PORT_OPT = "-p";
	final static int PORT_NULL = -2;

	public static void printUsage() {
		System.out.println("Usage:\n -c <localFolderPath>\n -s <serverURL1:serverPath1, serverURL2:serverPath2,...>\n ");

	}

	public static void main(final String[] args) throws IOException, OncRpcException, NoSuchAlgorithmException, NoSuchPaddingException {
		System.out.println("Starting Cryptobox");

		ArrayList<Thread> threads = new ArrayList<Thread>();
		
		//Absolutize the path
		String localPath = FileSystems.getDefault().getPath(localCryptoBoxPath).toAbsolutePath().normalize().toString();
		//System.out.println(localPath);
		RemoteFileService service = null;


		if(args.length != 0) {
			final ArrayList<SynchronizationService> services = new ArrayList<SynchronizationService>();
			
			final HashMap<String, String > optArgs = new HashMap<String, String>();
			for(int i = 0; i < args.length-1; i+=2) {
				final String opt = args[i].trim();
				final String arg = args[i+1].trim();
				optArgs.put(opt, arg);
			}
			
			String arg = null;
			int port = PORT_NULL;
			
			if((arg = optArgs.get(PORT_OPT)) != null) {
				port = Integer.parseInt(arg);	
			}
			
			
			if((arg = optArgs.get(CLIENT_OPT)) != null) {
				
				//set up client options
				final Path argPath = FileSystems.getDefault().getPath(arg).toAbsolutePath().normalize();

				if(Files.exists(argPath, LinkOption.NOFOLLOW_LINKS) && Files.isDirectory(argPath, LinkOption.NOFOLLOW_LINKS)) {
					System.out.println("OK Client: Using " + arg + " as CryptoBox folder.");
					localPath = argPath.toString();

					System.out.println("Encryption keys are in: " + localPath + "/.key");
					KeyManager.initInstance( localPath + "/.key");

				} else {
					System.out.println(arg + ": No such directory exists.");
					return;
				}
			} else {
				System.out.println("needs client arguemnts -c < >");
				printUsage();
				return;
			}

			
			if((arg = optArgs.get(SERVER_OPT)) != null) {
				
				//Set up server options
				final String[] servers = arg.split(",");
				for(int j = 0; j < servers.length; ++j) {

					final String[] serverArgs = servers[j].split(":");
					if(serverArgs.length < 2) {
						System.out.println("Wrong input format");
						printUsage();
						return;
					}

					System.out.println("Adding Server: " + serverArgs[0] + " with folder " + serverArgs[1]);
					
					NFSService nfs = null;
					if(port != PORT_NULL) {
						System.out.println("Port: " + port);
						nfs = new NFSService(serverArgs[0].trim(), serverArgs[1].trim(), localPath, port); 
						
					} else {
						nfs = new NFSService(serverArgs[0].trim(), serverArgs[1].trim(), localPath);
					}
					services.add(
							new  EncryptedSyncronizationService( //Comment to disable encryption
									nfs
									, new AES(),j) //Comment to disable encryption
							);
				}

				System.out.print("Ok Server: ");
				if(services.size() > 1) {
					System.out.println("Using Shamir Secret Sharing + Encryption + NFS");
					service = new SSSEncryptionSyncronizationService(services);
				} else {
					System.out.println("Using Encryption + NFS");
					service = services.get(0);
				}
			}  
			
			
			if((arg = optArgs.get(UI_OPT)) != null) {
				if(arg.equals("GUI")) {
					if(service == null) {
						printUsage();
						return;
					}
					System.out.println("+GUI");
					
					final RemoteFileService sCopy = service;
					final String pathCopy = localPath;
					threads.add(new Thread(  new Runnable() {
						@Override
						public void run() {
							new NFSNavigatorGUI(sCopy, pathCopy);
							
						}
					}));
				}
			}
		}  else {
			System.out.println("Using demo settings");
			//SynchronizationService service1 = new NFSService(nfsServer, nfsServerCryptoBoxPath1, localPath);
			//SynchronizationService service2 = new NFSService(nfsServer, nfsServerCryptoBoxPath2, localPath);
			final SynchronizationService service1 = new EncryptedSyncronizationService(new NFSService(nfsServer, nfsServerCryptoBoxPath1, localPath), new AES(),1);
			final SynchronizationService service2 = new EncryptedSyncronizationService(new NFSService(nfsServer, nfsServerCryptoBoxPath2, localPath), new AES(),2);
			//SynchronizationService service = new NFSService(nfsServer, nfsServerCryptoBoxPath, localPath);

			final ArrayList<SynchronizationService> services=new ArrayList<SynchronizationService>();
			services.add(service1);
			services.add(service2);
			service = new SSSEncryptionSyncronizationService(services);
		}

		final FileWatcher watcher = new FileWatcher(localPath);
		final FileSynchronizer synch = new FileSynchronizer( service );
		final NFSNavigator navigator = new NFSNavigator(service, localPath);

		watcher.addListener(synch);

		final ArrayList<File> fileToBeSynched = FileHelper.getNotSynchedFiles(localPath, service);

		//TODO BETTER eventually (encapsulate etc..)
		for(final File f: fileToBeSynched) {
			System.out.println("Synching " + f.getPath() + "....");
			synch.fileCreated(f);
		}


		threads.add( new Thread(watcher) );
		threads.add( new Thread(navigator));
		
		for(Thread t: threads) {
			t.start();
		}
	}
}


